package com.gitee.wsl.struct.compare.diff.patch

import com.gitee.wsl.struct.compare.diff.PatchFailedException


data class Change(
    val deltaType: DeltaType,
    val startOriginal: Int,
    val endOriginal: Int,
    val startRevised: Int,
    val endRevised: Int
)


/**
 * Specifies the type of the delta. There are three types of modifications from
 * the original to get the revised text.
 *
 * CHANGE: a block of data of the original is replaced by another block of data.
 * DELETE: a block of data of the original is removed
 * INSERT: at a position of the original a block of data is inserted
 *
 * to be complete there is also
 *
 * EQUAL: a block of data of original and the revised text is equal
 *
 * which is no change at all.
 *
 */
 enum class DeltaType {
    /**
     * A change in the original.
     */
    CHANGE,

    /**
     * A delete from the original.
     */
    DELETE,

    /**
     * An insert into the original.
     */
    INSERT,

    /**
     * An do nothing.
     */
    EQUAL
}

 sealed class Delta<T>( val type: DeltaType) {
     abstract val source: Chunk<T>
     abstract val target: Chunk<T>

    /**
     * Verify the chunk of this delta, to fit the target.
     * @param target
     * @throws PatchFailedException
     */
    @Throws(PatchFailedException::class)
    protected open fun verifyChunkToFitTarget(target: List<T>): VerifyChunk {
        return source.verify(target)
    }

    @Throws(PatchFailedException::class)
     open fun verifyAndApplyTo(target: MutableList<T>): VerifyChunk {
        val verify: VerifyChunk = verifyChunkToFitTarget(target)
        if (verify == VerifyChunk.OK) {
            applyTo(target)
        }
        return verify
    }

    @Throws(PatchFailedException::class)
    protected abstract fun applyTo(target: MutableList<T>)

     abstract fun restore(target: MutableList<T>)

    /**
     * Create a new delta of the actual instance with customized chunk data.
     */
     abstract fun withChunks(original: Chunk<T>, revised: Chunk<T>): Delta<T>
}
 data class ChangeDelta<T>(override val source: Chunk<T>, override val target: Chunk<T>) : Delta<T>(
     DeltaType.CHANGE
) {
    protected override fun applyTo(target: MutableList<T>) {
        val position: Int = source.position
        val size: Int = source.size()
        for (i in 0 until size) {
            target.removeAt(position)
        }
        this.target.lines.forEachIndexed { i, line ->
            target.add(position + i, line)
        }
    }

    override fun restore(target: MutableList<T>) {
        val position: Int = this.target.position
        val size: Int = this.target.size()
        for (i in 0 until size) {
            target.removeAt(position)
        }
        source.lines.forEachIndexed { i, line ->
            target.add(position + i, line)
        }
    }

    override fun withChunks(original: Chunk<T>, revised: Chunk<T>): Delta<T> = ChangeDelta(original, revised)
}

 data class DeleteDelta<T>(override val source: Chunk<T>, override val target: Chunk<T>) : Delta<T>(
     DeltaType.DELETE
) {
    protected override fun applyTo(target: MutableList<T>) {
        val position = source.position
        for (i in 0 until source.size()) {
            target.removeAt(position)
        }
    }

    override fun restore(target: MutableList<T>) {
        val position: Int = this.target.position
        val lines: List<T> = this.source.lines
        lines.forEachIndexed { i, line ->
            target.add(position + i, line)
        }
    }

    override fun withChunks(original: Chunk<T>, revised: Chunk<T>): Delta<T> = DeleteDelta(original, revised)
}

 data class InsertDelta<T>(override val source: Chunk<T>, override val target: Chunk<T>) : Delta<T>(
     DeltaType.INSERT
) {
    protected override fun applyTo(target: MutableList<T>) {
        val position = this.source.position
        this.target.lines.forEachIndexed { i, line ->
            target.add(position + i, line)
        }
    }

    override fun restore(target: MutableList<T>) {
        val position = this.target.position
        for (i in 0 until this.target.size()) {
            target.removeAt(position)
        }
    }

    override fun withChunks(original: Chunk<T>, revised: Chunk<T>): Delta<T> = InsertDelta(original, revised)
}

 data class EqualDelta<T>(override val source: Chunk<T>, override val target: Chunk<T>) : Delta<T>(
     DeltaType.EQUAL
) {
    protected override fun applyTo(target: MutableList<T>): Unit = Unit

    override fun restore(target: MutableList<T>): Unit = Unit

    override fun withChunks(original: Chunk<T>, revised: Chunk<T>): Delta<T> = EqualDelta(original, revised)
}
